﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Windows.Media;
using Ninject;
using SydEnergiPrototype.DataRepositoryServiceRef;
using SydEnergiPrototype.Model;

namespace SydEnergiPrototype.Logic
{
    public class EntryParser : Parser
    {
        private readonly IBrushParser _brushParser;
        private readonly IParserFactory _parserFactory;

        [Inject]
        public EntryParser(IBrushParser brushParser, IParserFactory parserFactory)
        {
            if(brushParser == null)
                throw new ArgumentNullException("brushParser");
            _brushParser = brushParser;

            if(parserFactory == null)
                throw new ArgumentNullException();
            _parserFactory = parserFactory;
        }

        protected override bool IsPrimitive(PropertyInfo property)
        {
            if (property.Name == "AddressDto" || property.Name == "EntryAddress" || property.PropertyType == typeof(History) ||
                property.Name == "Fiber" || property.Name == "Tube" || property.PropertyType == typeof(ObservableCollection<HistoryEntryDto>))
            {
                return false;
            }

            return true;
        }

        protected override void SerializeNonPrimitive(PropertyInfo property, object source, object returnObject)
        {
            object o = property.GetValue(source, null);

            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var returnObjectInfos = returnObject.GetType().GetProperties(flags);

            if(property.Name == "EntryAddress")
            {
                const string dtoEntryAddressName = "AddressDto";
                if (returnObjectInfos.Any(i => i.Name == dtoEntryAddressName))
                {
                    foreach (var propertyInfo in returnObjectInfos.Where(a => a.Name == dtoEntryAddressName))
                    {
                        if (o is IAddress)
                        {
                            var parser = _parserFactory.Make(o.GetType());
                            o = parser.ParseToDto<IAddress, AddressDto>(o as IAddress);
                        }
                            
                        propertyInfo.SetValue(returnObject, o, null);
                    }
                }
                else
                {
                    foreach (var propertyInfo in returnObjectInfos.Where(a => a.Name == property.Name))
                    {
                        propertyInfo.SetValue(returnObject, o, null);
                    }
                }
            }
            else if (o is SolidColorBrush)
            {
                o = _brushParser.ParseToString(o as SolidColorBrush);
                foreach (var returnObjectInfo in returnObjectInfos.Where(i => i.Name == property.Name))
                {
                    returnObjectInfo.SetValue(returnObject, o, null);
                }
            }
            else if(o is History)
            {
                foreach (var propertyInfo in returnObjectInfos.Where(a => a.Name == property.Name))
                {
                    if (propertyInfo.PropertyType != typeof(History))
                    {
                        var parser = _parserFactory.Make((o as History).First().GetType());
                        var history = new ObservableCollection<HistoryEntryDto>();
                        foreach (var h in ((IEntry) source).EntryHistory)
                        {
                            history.Add(parser.ParseToDto<HistoryEntry, HistoryEntryDto>(h as HistoryEntry));
                        }
                        propertyInfo.SetValue(returnObject, history, null);
                        return;
                    }
                    propertyInfo.SetValue(returnObject, o, null);
                }
            }
        }

        protected override void DeserializeNonPrimitive(PropertyInfo property, object source, object returnObject)
        {
            object o = property.GetValue(source, null);
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var returnObjectInfos = returnObject.GetType().GetProperties(flags);

            if(property.Name == "AddressDto")
            {
                const string EntryAddressName = "EntryAddress";
                foreach (var entryInfo in returnObjectInfos.Where(e => e.Name == EntryAddressName))
                {
                    if (o is AddressDto)
                    {
                        var parser = _parserFactory.Make(o.GetType());
                        o = parser.ParseFromDto<AddressDto, Address>(o as AddressDto);
                    }
                    entryInfo.SetValue(returnObject, o, null);
                }
            }
            else if(property.Name == "EntryAddress")
            {
                foreach (var returnObjectInfo in returnObjectInfos.Where(e => e.Name == "EntryAddress"))
                {
                    returnObjectInfo.SetValue(returnObject, o, null);
                }
            }
            else if(property.Name == "Fiber" || property.Name == "Tube")
            {
                o = _brushParser.ParseToBrush(o as string);
                foreach (var returnObjectInfo in returnObjectInfos.Where(i => i.Name == property.Name))
                {
                    returnObjectInfo.SetValue(returnObject, o, null);
                }
            }
            else if (o is ObservableCollection<HistoryEntryDto>)
            {
                foreach (var propertyInfo in returnObjectInfos.Where(a => a.Name == property.Name))
                {
                    var history = new History();
                    var parser = _parserFactory.Make((o as ObservableCollection<HistoryEntryDto>).First().GetType());
                    foreach (var h in (ObservableCollection<HistoryEntryDto>)o)
                    {
                        history.Add(parser.ParseFromDto<HistoryEntryDto, HistoryEntry>(h));
                    }
                    propertyInfo.SetValue(returnObject, history, null);
                }
            }
            else if(o is History && source is SerializableEntry)
            {
                foreach (var propertyInfo in returnObjectInfos.Where(a => a.Name == property.Name))
                {
                    propertyInfo.SetValue(returnObject, o, null);
                }
            }
        }
    }
}
